package tech

import (
	"ssp/set"
)

func getCandCellsInHouse(house *House) [9][]*Cell {
	candCells := [9][]*Cell{}
	for _, cell := range house.cells {
		if cell.solved() {
			continue
		}
		for _, cand := range cell.cands.GetList() {
			candCells[cand] = append(candCells[cand], cell)
		}
	}
	return candCells
}

func sameRow(cells []*Cell) bool {
	rowNum := cells[0].rowNum
	for _, cell := range cells[1:] {
		if cell.rowNum != rowNum {
			return false
		}
	}
	return true
}
func sameCol(cells []*Cell) bool {
	colNum := cells[0].colNum
	for _, cell := range cells[1:] {
		if cell.colNum != colNum {
			return false
		}
	}
	return true
}

func sameBox(cells []*Cell) bool {
	boxNum := cells[0].boxNum
	for _, cell := range cells[1:] {
		if cell.boxNum != boxNum {
			return false
		}
	}
	return true
}

func (s *Solve) Pointing(g *Grid) (used bool, results []*Result) {
	return g.pointing()
}
func (g *Grid) pointing() (used bool, results []*Result) {
	for _, box := range g.boxes {
		for cand, cells := range getCandCellsInHouse(&box) {
			if len(cells) < 2 {
				continue
			}
			var ok bool
			var rmCells []*Cell
			result := new(Result)
			used = false
			if sameRow(cells) {
				if ok, rmCells = g.clearupHouseBut(
					cand, g.rows[cells[0].rowNum], cells); ok {
					used = true
					if OUTPUTRESULT {
						result.addHelpHouses([]*House{&box})
						result.addHelpHouses([]*House{&g.rows[cells[0].rowNum]})
						result.setStrategyName("Row Pointing")
						result.addRemoved(set.NewSet(cand), rmCells)
						result.setHelpCands(set.NewSet(cand), cells)
						results = append(results, result)
					}
					if CHANGE {
						for _, rmCell := range rmCells {
							rmCell.removeCand(cand)
						}
					}
					if !EXHAUSTIVE {
						return
					}
				}
			} else if sameCol(cells) {
				if ok, rmCells = g.clearupHouseBut(
					cand, g.cols[cells[0].colNum], cells); ok {
					used = true
					if OUTPUTRESULT {
						result.addHelpHouses([]*House{&box})
						result.addHelpHouses([]*House{&g.cols[cells[0].colNum]})
						result.setStrategyName("Col Pointing")
						result.addRemoved(set.NewSet(cand), rmCells)
						result.setHelpCands(set.NewSet(cand), cells)
						results = append(results, result)
					}
					if CHANGE {
						for _, rmCell := range rmCells {
							rmCell.removeCand(cand)
						}
					}
					if !EXHAUSTIVE {
						return
					}
				}
			} else {
				continue
			}
		}
	}
	return len(results) != 0, results
}

func (g *Grid) clearupHouseBut(cand int, house House, butCells []*Cell) (
	used bool, rmCells []*Cell) {
	for _, cell := range house.cells {
		if cell.solved() || cell.isMemberOf(butCells) {
			continue
		}
		if cell.canRemoveCand(cand) {
			used = true
			rmCells = append(rmCells, cell)
		}
	}
	return
}

func (g *Grid) claimingLine(line *House, sname string) (used bool, results []*Result) {
	for cand, cells := range getCandCellsInHouse(line) {

		if len(cells) < 2 {
			continue
		}
		if !sameBox(cells) {
			continue
		}
		if ok, rmCells := g.clearupHouseBut(
			cand, g.boxes[cells[0].boxNum], cells); ok {
			used = true
			result := new(Result)
			if OUTPUTRESULT {
				result.addRemoved(set.NewSet(cand), rmCells)
				result.setHelpCands(set.NewSet(cand), cells)
				result.addHelpHouses([]*House{line})
				result.addHelpHouses([]*House{&g.boxes[cells[0].boxNum]})
				result.setStrategyName(sname)
				results = append(results, result)
			}
			if CHANGE {
				removeCands(set.NewSet(cand), rmCells)
			}
			if !EXHAUSTIVE {
				return
			}
		}
	}
	return
}

func (s *Solve) ClaimingRow(g *Grid) (used bool, results []*Result) {
	for _, row := range g.rows {
		ok, rs := g.claimingLine(&row, "Row Claiming")
		//fmt.Println(result)
		if ok {
			used = true
			if OUTPUTRESULT {
				results = append(results, rs...)
			}
			if !EXHAUSTIVE {
				return
			}
		}
	}

	return
}

func (s *Solve) ClaimingCol(g *Grid) (used bool, results []*Result) {
	for _, col := range g.cols {
		if ok, rs := g.claimingLine(&col, "Col Claiming"); ok {
			used = true
			if OUTPUTRESULT {
				results = append(results, rs...)
			}
			if !EXHAUSTIVE {
				return
			}
		}
	}

	return
}
